﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;

namespace Gakita.Model
{
    /* This class contains information about the current level. It also contains methods for checking collision with objects in the level */
    class Level
    {
        public enum Tile
        {
            T_EMPTY = 0,
            T_NONEMPTY = 1,
            T_NONEMPTY2 = 2,
            T_NONEMPTYC1 = 3,
            T_NONEMPTYC2 = 4,
            T_NONEMPTYC3 = 5,
            T_NONEMPTY3 = 6,
            T_NONEMPTY4 = 7,
            T_NONEMPTY5 = 8,
            T_NONEMPTY6 = 9,
            T_NONEMPTY7 = 10,
            T_NONEMPTY8 = 11,
            T_NONEMPTY9 = 12,
            T_NONEMPTY10 = 13,

        };

        public enum Levels
        {
            first = 1,
            second = 2,
            third = 3,
            fourth = 4,
        }

        private const string _firstLevelFileName                        =       "firstlevel.txt";
        private const string _secondLevelFileName                       =       "secondlevel.txt";
        private const string _thirdLevelFileName                        =       "thirdlevel.txt";
        private const string _fourthLevelFileName                       =       "fourthlevel.txt";

        private Levels _currentLevel                                    =       Levels.first;

        private Vector2 _cakeBottomCenterPosition                       =       Vector2.Zero;
        private Vector2 _cakeSize                                       =       new Vector2(0.8f, 0.5f);

        public const int _levelWidth                                    =       110;
        public const int _levelHeight                                   =       20;

        public List<Enemies.Rabbit> _rabbits                            =       new List<Enemies.Rabbit>();
        public List<Enemies.SpikyThing> _spikyThings                    =       new List<Enemies.SpikyThing>();
        public List<Enemies.FlyingEnemy> _flyingEnemies                 =       new List<Enemies.FlyingEnemy>();
        public List<Enemies.GiantRabbit> _giantRabbits                  =       new List<Enemies.GiantRabbit>();

        public List<Miscellaneous.FlyingPlatform> _flyingPlatforms      =       new List<Miscellaneous.FlyingPlatform>();

        public Tile[,] _tiles                                           =       new Tile[_levelWidth, _levelHeight];

        public Level()
        {
            DoCurrentLevel();
        }

        /* This method generates a level. Which level depends on the _currentlevel variable */
        public void DoCurrentLevel()
        {
            switch(_currentLevel)
            {
                case Levels.first:
                    GenerateLevel(_firstLevelFileName);
                    break;
                case Levels.second:
                    GenerateLevel(_secondLevelFileName);
                    break;
                case Levels.third:
                    GenerateLevel(_thirdLevelFileName);
                    break;
                case Levels.fourth:
                    GenerateLevel(_fourthLevelFileName);
                    break;
            }
                
        }

        public Levels GetCurrentLevel()
        {
            return _currentLevel;
        }

        /* This method sets a new level, and then generates it */
        public void SetCurrentLevel(Levels aNewLevel)
        {
            _currentLevel = aNewLevel;
            DoCurrentLevel();
        }

        /* This method generates a new level by reading the appropriate text file character by character and creating different objects                 depending on which character it reads */
        private void GenerateLevel(string aLevelFileName)
        {
            // The lists containing enemies and platforms are cleared to ensure that the enemies from an old level doesn't remain 
            // when we generate a new one
            _rabbits.Clear();
            _spikyThings.Clear();
            _flyingEnemies.Clear();
            _flyingPlatforms.Clear();
            _giantRabbits.Clear();

            StreamReader LevelReader = new StreamReader(aLevelFileName);

            for (int y = 0; y < _levelHeight; y++)
            {   
                for (int x = 0; x < _levelWidth; x++)
                {
                    // Read a character from the levelfile
                    int currentchar = LevelReader.Read();

                    // Since the textfiles contains CRLF, we need to ignore those. We will simply move straight to the next character
                    while (currentchar == 13 || currentchar == 10)
                    {
                        currentchar = LevelReader.Read();
                    }

                    // TILES

                    // If f
                    if (currentchar == 102)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY10;
                    }

                    // If d
                    if (currentchar == 100)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY9;
                    }

                    // If s
                    if (currentchar == 115)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY8;
                    }

                    // If a
                    if (currentchar == 97)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY7;
                    }

                    // If 9
                    if (currentchar == 57)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY6;
                    }

                    // If 8
                    if (currentchar == 56)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY5;
                    }

                    // If 7
                    if (currentchar == 55)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY4;
                    }

                    // If 6
                    if (currentchar == 54)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY3;
                    }

                    // If 5
                    if (currentchar == 53)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY2;
                    }

                    // If 4
                    if (currentchar == 52)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTYC3;
                    }

                    // If 3
                    if (currentchar == 51)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTYC2;
                    }

                    // If 2
                    if (currentchar == 50)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTYC1;
                    }

                    // If 1
                    if (currentchar == 49)
                    {
                        _tiles[x, y] = Tile.T_NONEMPTY;
                    }
                    // If 0
                    else if (currentchar == 48)
                    {
                        _tiles[x, y] = Tile.T_EMPTY;
                    }

                    // CAKE

                    // If c
                    else if (currentchar == 99)
                    {
                        _cakeBottomCenterPosition = new Vector2(x + 0.5f, y + 1);
                        _tiles[x, y] = Tile.T_EMPTY;
                    }

                    // ENEMIES

                    // if e
                    else if (currentchar == 101)
                    {
                        Vector2 enemyPosition = new Vector2(x + 0.5f, y + 1);
                        _rabbits.Add(new Enemies.Rabbit(enemyPosition));
                        _tiles[x, y] = Tile.T_EMPTY;
                    }
                    // if E
                    else if (currentchar == 69)
                    {
                        Vector2 enemyPosition = new Vector2(x + 0.5f, y + 0.5f);
                        _spikyThings.Add(new Enemies.SpikyThing(enemyPosition, Enemies.SpikyThing.Direction.Vertical));
                        _tiles[x, y] = Tile.T_EMPTY;
                    }
                    // if S
                    else if (currentchar == 83)
                    {
                        Vector2 enemyPosition = new Vector2(x + 0.5f, y + 0.5f);
                        _spikyThings.Add(new Enemies.SpikyThing(enemyPosition, Enemies.SpikyThing.Direction.Horizontal));
                        _tiles[x, y] = Tile.T_EMPTY;
                    }
                    // if F
                    else if (currentchar == 70)
                    {
                        // Two flying enemies are spawned at the same location, moving in opposite directions.
                        Vector2 enemyPosition = new Vector2(x + 0.5f, y + 1);
                        _flyingEnemies.Add(new Enemies.FlyingEnemy(enemyPosition, new Vector2(2, 0)));
                        _flyingEnemies.Add(new Enemies.FlyingEnemy(enemyPosition, new Vector2(-2, 0)));
                        _tiles[x, y] = Tile.T_EMPTY;
                    }

                    // if G
                    else if (currentchar == 71)
                    {
                        Vector2 enemyPosition = new Vector2(x + 0.5f,y);
                        _giantRabbits.Add(new Enemies.GiantRabbit(enemyPosition));
                        _tiles[x, y] = Tile.T_EMPTY;
                    }

                    // FLYING PLATFORMS
                    // There are two different kinds of flying platforms, 
                    // the only difference is how far they travel before they turn around.

                    // if y
                    if (currentchar == 121)
                    {
                        _flyingPlatforms.Add(new Miscellaneous.FlyingPlatform(new Vector2(x,y), 3));
                        _tiles[x, y] = Tile.T_EMPTY;
                    }
                    // if Y
                    if (currentchar == 89)
                    {
                        _flyingPlatforms.Add(new Miscellaneous.FlyingPlatform(new Vector2(x, y), 4));
                        _tiles[x, y] = Tile.T_EMPTY;
                    }
                }
            }
  
            LevelReader.Close();
        }

        /* This method respawns all the rabbits in the currentlevel. Called after the player dies */
        public void RespawnRabbits()
        {
            foreach (Enemies.GiantRabbit gRabbit in _giantRabbits)
            {
                gRabbit.Reset();
            }
            foreach(Enemies.Rabbit rabbit in _rabbits)
            {
                rabbit.Reset();
            }
        }

        /* This method checks if the player is colliding with the cake, or not */
        public bool isPlayerCollidingWithCake(Vector2 aPlayerBottomCenterPosition, Vector2 aPlayerSize)
        {
            if (aPlayerBottomCenterPosition.X + aPlayerSize.X / 2 > _cakeBottomCenterPosition.X - _cakeSize.X / 2 &&                                        aPlayerBottomCenterPosition.X - aPlayerSize.X / 2 < _cakeBottomCenterPosition.X + _cakeSize.X / 2)
            {
                if (aPlayerBottomCenterPosition.Y - aPlayerSize.Y < _cakeBottomCenterPosition.Y  &&                                                              aPlayerBottomCenterPosition.Y > _cakeBottomCenterPosition.Y - _cakeSize.Y)
                {
                    return true;
                }
            }
                
            return false;    
        }

        /* This method checks if the player is colliding with a rabbit. It also checks how the collision happened, to see if the rabbit is              supposed to die. If so, the isTheRabbitSupposedToDieNow variable is set to true. */
        public bool IsPlayerCollidingWithARabbit(Vector2 aPlayerBottomCenterPosition, Vector2 aPlayerSize, 
                                                   Vector2 aRabbitBottomCenterPosition, Vector2 aRabbitSize, 
                                                   out bool aIsTheRabbitSupposedToDieNow)
        {
            bool ret = false;
            aIsTheRabbitSupposedToDieNow = false;

            if (aPlayerBottomCenterPosition.X + aPlayerSize.X / 2 > aRabbitBottomCenterPosition.X - aRabbitSize.X / 2 &&
                aPlayerBottomCenterPosition.X - aPlayerSize.X / 2 < aRabbitBottomCenterPosition.X + aRabbitSize.X / 2)
            {
                if (aPlayerBottomCenterPosition.Y - aPlayerSize.Y < aRabbitBottomCenterPosition.Y && 
                    aPlayerBottomCenterPosition.Y > aRabbitBottomCenterPosition.Y - aRabbitSize.Y)
                {
                    ret = true;

                    // If the players y-position is higher then the middle of the rabbit, we assume the player jumped on the rabbit
                    if(aPlayerBottomCenterPosition.Y < aRabbitBottomCenterPosition.Y - aRabbitSize.Y / 2)
                    {
                        aIsTheRabbitSupposedToDieNow = true;
                    }
                }
            }

            return ret;
        }

        /* This method checks if the player is colliding with a SpikyThing */
        public bool IsPlayerCollidingWithASpikyThing(Vector2 aPlayerBottomCenterPosition, Vector2 aPlayerSize,
                                                       Vector2 aSpikyThingCenterPosition, Vector2 aSpikyThingSize)
        {
            if (aPlayerBottomCenterPosition.X + aPlayerSize.X / 2 > aSpikyThingCenterPosition.X - aSpikyThingSize.X / 2 &&
               aPlayerBottomCenterPosition.X - aPlayerSize.X / 2 < aSpikyThingCenterPosition.X + aSpikyThingSize.X / 2)
            {
                if(aPlayerBottomCenterPosition.Y - aPlayerSize.Y < aSpikyThingCenterPosition.Y + aSpikyThingSize.Y / 2 &&
                   aPlayerBottomCenterPosition.Y > aSpikyThingCenterPosition.Y - aSpikyThingSize.Y /2)
                {
                    return true;
                }
            }
            return false;
        }

        /* This method checks if the player is colliding with a GiantRabbit */
        public bool IsPlayerCollidingWithAGiantRabbit(Vector2 aPlayerBottomCenterPosition, Vector2 aPlayerSize,
                                                       Vector2 aGiantRabbitBottomCenterPosition, Vector2 aGiantRabbitSize)
        {
            if (aPlayerBottomCenterPosition.X + aPlayerSize.X / 2 > aGiantRabbitBottomCenterPosition.X - aGiantRabbitSize.X / 2 &&
               aPlayerBottomCenterPosition.X - aPlayerSize.X / 2 < aGiantRabbitBottomCenterPosition.X + aGiantRabbitSize.X / 2)
            {
                if (aPlayerBottomCenterPosition.Y - aPlayerSize.Y < aGiantRabbitBottomCenterPosition.Y  &&
                   aPlayerBottomCenterPosition.Y > aGiantRabbitBottomCenterPosition.Y - aGiantRabbitSize.Y)
                {
                    return true;
                }
            }
            return false;
        }

        /* This method checks if the player is colliding with any of the level tiles, or a Flying Platform. */
        public bool IsCollidingAt(Vector2 aPosition, Vector2 aSize, ref bool aIsStandingOnFlyingPlatform,
                                  ref Miscellaneous.FlyingPlatform aPlatform)
        {
            Vector2 topLeft = new Vector2(aPosition.X - aSize.X / 2.0f, aPosition.Y - aSize.Y);
            Vector2 bottomRight = new Vector2(aPosition.X + aSize.X / 2.0f, aPosition.Y);

            // Level
            for (int x = 0; x < _levelWidth; x++)
            {
                for (int y = 0; y < _levelHeight; y++)
                {
                    if (bottomRight.X < (float)x)
                        continue;
                    if (bottomRight.Y < (float)y)
                        continue;
                    if (topLeft.X > (float)x + 1.0f)
                        continue;
                    if (topLeft.Y > (float)y + 1.0f)
                        continue;

                    if ((int)_tiles[x, y] > 0)
                    {
                        return true;
                    }
                }
            }
            // Flying Platforms
            foreach (Miscellaneous.FlyingPlatform flyingPlatform in _flyingPlatforms)
            {
                Vector2 platformTopLeftPosition = flyingPlatform.GetPosition();
                Vector2 platformBottomRightPosition = platformTopLeftPosition + flyingPlatform.GetSize();

                if (bottomRight.X < platformTopLeftPosition.X)
                    continue;
                if (bottomRight.Y < platformTopLeftPosition.Y)
                    continue;
                if (topLeft.X > platformBottomRightPosition.X)
                    continue;
                if (topLeft.Y > platformBottomRightPosition.Y)
                    continue;

                    aPlatform = flyingPlatform;
                    aIsStandingOnFlyingPlatform = true;
                
                return true;
            }
            return false;
        }

        public Vector2 GetCakePosition()
        {
            return _cakeBottomCenterPosition;
        }
    }
}
